package com.ruoyi.framework.interceptor;

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.schema.Table;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DatabaseNamePrefixInterceptor implements InnerInterceptor, Interceptor {

    private String databaseName;

    public DatabaseNamePrefixInterceptor(Connection connection) {
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            this.databaseName = metaData.getURL().split("/")[3].split("\\?")[0];
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        MetaObject metaObject = SystemMetaObject.forObject(sh);
        MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        BoundSql boundSql = sh.getBoundSql();
        String originalSql = boundSql.getSql();
        try {
            Statement statement = CCJSqlParserUtil.parse(originalSql);
            processStatement(statement);
            String newSql = statement.toString();
            setFieldValue(boundSql, "sql", newSql);
        } catch (JSQLParserException e) {
            e.printStackTrace();
        }
    }

    private void processStatement(Statement statement) {
        if (statement instanceof Select) {
            processSelect((Select) statement);
        } else if (statement instanceof Insert) {
            processInsert((Insert) statement);
        } else if (statement instanceof Delete) {
            processDelete((Delete) statement);
        } else if (statement instanceof Update) {
            processUpdate((Update) statement);
        }
    }

    private void processSelect(Select select) {
        SelectBody selectBody = select.getSelectBody();
        if (selectBody instanceof PlainSelect) {
            PlainSelect plainSelect = (PlainSelect) selectBody;
            // 处理 FROM 子句中的表
            FromItem fromTable =  plainSelect.getFromItem();
            if (fromTable != null) {
                if(fromTable instanceof Table){
                    Table table = (Table) plainSelect.getFromItem();
                    processTable(table);
                }else if(fromTable instanceof SubSelect){
                    SelectBody body = ((SubSelect) fromTable).getSelectBody();
                    if (body instanceof PlainSelect) {
                        Select st = new Select();
                        st.setSelectBody(body);
                        processSelect(st);
                    }
                }
            }
            // 处理 LEFT JOIN 子句中的表
            List<Join> joins = plainSelect.getJoins();
            if (joins != null) {
                for (Join join : joins) {
                    if (join.getRightItem() instanceof Table) {
                        Table joinTable = (Table) join.getRightItem();
                        processTable(joinTable);
                    } else if (join.getRightItem() instanceof SubSelect) {
                        processSelect((Select) ((SubSelect) join.getRightItem()).getSelectBody());
                    }
                }
            }
            // 处理子查询
            List<SelectItem> selectItems = plainSelect.getSelectItems();
            if (selectItems != null) {
                for (SelectItem selectItem : selectItems) {
                    if (selectItem instanceof SubSelect) {
                        processSelect((Select) ((SubSelect) selectItem).getSelectBody());
                    }
                }
            }
            if (plainSelect.getWhere() != null) {
                processExpression(plainSelect.getWhere().toString());
            }
        } else if (selectBody instanceof SetOperationList) {
            SetOperationList setOperationList = (SetOperationList) selectBody;
            List<SelectBody> selectBodies = setOperationList.getSelects();
            for (SelectBody body : selectBodies) {
                if (body instanceof PlainSelect) {
                    Select st = new Select();
                    st.setSelectBody(body);
                    processSelect(st);
                }
            }
        }
    }

    private void processInsert(Insert insert) {
        Table table = insert.getTable();
        if (table != null) {
            processTable(table);
        }
        if (insert.getSelect() != null) {
            processSelect(insert.getSelect());
        }
    }

    private void processDelete(Delete delete) {
        Table table = delete.getTable();
        if (table != null) {
            processTable(table);
        }
        if (delete.getWhere() != null) {
            processExpression(delete.getWhere().toString());
        }
    }

    private void processUpdate(Update update) {
        Table table = update.getTable();
        if (table != null) {
            processTable(table);
        }
        // 处理 UPDATE 语句中的 JOIN 部分
        if (update.getFromItem() != null) {
            if (update.getFromItem() instanceof Table) {
                Table fromTable = (Table) update.getFromItem();
                processTable(fromTable);
            } else if (update.getFromItem() instanceof SubSelect) {
                processSelect((Select) ((SubSelect) update.getFromItem()).getSelectBody());
            }
        }
        List<Join> joins = update.getJoins();
        if (joins != null) {
            for (Join join : joins) {
                if (join.getRightItem() instanceof Table) {
                    Table joinTable = (Table) join.getRightItem();
                    processTable(joinTable);
                } else if (join.getRightItem() instanceof SubSelect) {
                    processSelect((Select) ((SubSelect) join.getRightItem()).getSelectBody());
                }
            }
        }
        if (update.getWhere() != null) {
            processExpression(update.getWhere().toString());
        }
    }

    private void processExpression(String expression) {
        try {
            Statement statement = CCJSqlParserUtil.parse(expression);
            if (statement instanceof Select) {
                processSelect((Select) statement);
            }
        } catch (JSQLParserException e) {
            // 忽略非 SELECT 语句的异常
        }
    }

    private void processTable(Table table) {
        String tableName = table.getName();
        if (!tableName.toLowerCase().contains(".")) {
            table.setName(databaseName + "." + tableName);
        }
    }

    private void setFieldValue(Object obj, String fieldName, Object value) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        BoundSql boundSql = statementHandler.getBoundSql();
        String originalSql = boundSql.getSql();
        try {
            Statement statement = CCJSqlParserUtil.parse(originalSql);
            processStatement(statement);
            String newSql = statement.toString();
            setFieldValue(boundSql, "sql", newSql);
        } catch (JSQLParserException e) {
            e.printStackTrace();
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
        // 可用于读取配置文件中的属性
    }
}